package com.example.literj.kt.internal.disposables

import com.example.literj.kt.disposables.Disposable
import com.example.literj.kt.disposables.DisposableContainer
import com.example.literj.kt.exceptions.CompositeException
import com.example.literj.kt.internal.util.ExceptionHelper
import com.example.literj.kt.internal.util.OpenHashSet
import java.util.*

class CompositeDisposable : Disposable, DisposableContainer {
    private var resources: OpenHashSet? = null

    @Volatile
    private var disposed = false

    override fun dispose() {
        if (disposed) {
            return
        }
        var set: OpenHashSet?
        synchronized(this) {
            if (disposed) {
                return
            }
            disposed = true
            set = resources
            resources = null
        }

        dispose(set)
    }

    override fun isDisposed() = disposed

    override fun add(d: Disposable): Boolean {
        Objects.requireNonNull(d, "disposable is null")
        if (!disposed) {
            synchronized(this) {
                if (!disposed) {
                    var set = resources
                    if (set == null) {
                        set = OpenHashSet()
                        resources = set
                    }
                    set.add(d)
                    return true
                }
            }
        }
        d.dispose()
        return false
    }

    override fun remove(d: Disposable): Boolean {
        if (delete(d)) {
            d.dispose()
            return true
        }
        return false
    }

    override fun delete(d: Disposable): Boolean {
        Objects.requireNonNull(d, "disposable is null")
        if (disposed) {
            return false
        }
        synchronized(this) {
            if (disposed) {
                return false
            }
            val set = resources
            if (set == null || !set.remove(d)) {
                return false
            }
        }
        return true
    }

    fun dispose(set: OpenHashSet?) {
        if (set == null) {
            return
        }
        var errors: MutableList<Throwable?>? = null
        val array = set.keys()
        for (o in array) {
            if (o is Disposable) {
                try {
                    o.dispose()
                } catch (ex: Throwable) {
                    if (errors == null) {
                        errors = ArrayList()
                    }
                    errors.add(ex)
                }
            }
        }
        if (errors != null) {
            if (errors.size == 1) {
                throw ExceptionHelper.wrapOrThrow(errors[0])
            }
            throw CompositeException(errors)
        }
    }
}